Vegetation & Moisture • Remote Sensing Index

NDMI – Normalized Difference Moisture Index NDMI

The Normalized Difference Moisture Index (NDMI) is a spectral index that uses Near-Infrared (NIR) and Shortwave Infrared (SWIR) bands to highlight canopy and surface moisture. It is widely used for soil moisture assessment, vegetation water stress and drought monitoring.

1. Concept & Formula

NDMI exploits the different responses of NIR and SWIR reflectance to moisture content in vegetation and soils. Moist targets tend to have higher NIR and lower SWIR reflectance, while dry or stressed surfaces show the opposite behaviour.

Mathematical Definition

The standard NDMI formulation is:

NDMI = (NIR - SWIR) / (NIR + SWIR)
Typical range: -1 to +1
Higher values → higher moisture content
Lower values → dry or water-stressed conditions

Main Applications

  • Monitoring crop and canopy water status
  • Drought and vegetation stress mapping
  • Supporting irrigation planning and scheduling
  • Complementing NDVI for better moisture interpretation
Moisture / Water Stress Vegetation Health Drought Analysis

2. Data & Bands for NDMI

Common Sensors & Bands

  • Sentinel-2 (ESA) – 10–20 m
    • NIR: B8 (~842 nm)
    • SWIR1: B11 (~1610 nm)
  • Landsat 8/9 OLI – 30 m
    • NIR: B5
    • SWIR1: B6

Good Practice

  • Use surface reflectance products (atmospherically corrected).
  • Mask out clouds, cirrus and cloud shadows before computing NDMI.
  • Analyse NDMI alongside NDVI and land cover maps for better interpretation.

Interpretation Hints

  • High positive NDMI → healthy, moist vegetation or wet surfaces.
  • Near-zero values → moderate moisture, transitional conditions.
  • Negative values → very dry soil, bare surfaces or stressed vegetation.

3. Google Earth Engine Code – NDMI (Sentinel-2)

Draw your Area of Interest (AOI) as geometry, then paste and run this script in the Google Earth Engine Code Editor.


// NDMI (Normalized Difference Moisture Index) using Sentinel-2 surface reflectance
// -------------------------------------------------------
// 1. Define Area of Interest (AOI)
//    - Draw a polygon/rectangle on the map and name it "geometry"
var roi = geometry;  // use the drawn geometry as AOI

// 2. Define time period
var startDate = '2023-05-01';
var endDate   = '2023-09-30';

// 3. Load Sentinel-2 SR collection and pre-filter
var s2 = ee.ImageCollection('COPERNICUS/S2_SR')
  .filterBounds(roi)
  .filterDate(startDate, endDate)
  .filter(ee.Filter.lt('CLOUDY_PIXEL_PERCENTAGE', 20));

// 4. Create a median composite and select NIR & SWIR1 bands
//    Sentinel-2: NIR = B8, SWIR1 = B11
var composite = s2
  .select(['B8','B11'])
  .median()
  .clip(roi);

// 5. Compute NDMI = (NIR - SWIR) / (NIR + SWIR)
var ndmi = composite.normalizedDifference(['B8', 'B11']).rename('NDMI');

// 6. Define visualisation parameters
var ndmiVis = {
  min: -1.0,
  max: 1.0,
  palette: [
    '#8c510a', // very dry
    '#d8b365',
    '#f6e8c3',
    '#c7eae5',
    '#5ab4ac',
    '#01665e'  // very moist
  ]
};

// 7. Center map and add NDMI layer
Map.centerObject(roi, 10);
Map.addLayer(ndmi, ndmiVis, 'NDMI (Sentinel-2)', true);

// Optional: add a true color background
var s2_rgb = ee.ImageCollection('COPERNICUS/S2_SR')
  .filterBounds(roi)
  .filterDate(startDate, endDate)
  .filter(ee.Filter.lt('CLOUDY_PIXEL_PERCENTAGE', 20))
  .select(['B4','B3','B2']) // RGB
  .median()
  .clip(roi);

Map.addLayer(s2_rgb, {min: 0, max: 3000}, 'True Color (RGB)', false);

// 8. Export NDMI as GeoTIFF to Google Drive
Export.image.toDrive({
  image: ndmi,
  description: 'NDMI_Export',
  fileNamePrefix: 'NDMI_Export',
  region: roi,
  scale: 20,            // Sentinel-2 NDMI at ~20 m (driven by B11 resolution)
  crs: 'EPSG:4326',
  maxPixels: 1e13
});